home *** CD-ROM | disk | FTP | other *** search
- // Wais.m
- //
- // Free software created 1 Feb 1992
- // by Paul Burchard <burchard@math.utah.edu>.
- // Incorporating:
- /*
- WIDE AREA INFORMATION SERVER SOFTWARE:
- No guarantees or restrictions. See the readme file for the full standard
- disclaimer.
-
- This is part of the [NeXTstep] user-interface for the WAIS software.
- Do with it as you please.
-
- Version 0.82
- Wed Apr 24 1991
-
- jonathan@Think.COM
-
- */
- //
- // Here are the general utility hacks that didn't belong anywhere else...
- // See Wais.h for more information.
-
-
- #import "Wais.h"
-
-
- // These global vars are needed by the WAIS library.
- // We also use them for non-NeXTstep error messages.
- FILE *logfile = stderr;
- char *log_file_name = NULL;
-
- // List of all Wais objects and their keys.
- static id convertKeyToObject;
- static id globalObjectList;
-
- // Search path for objects---subclass should override.
- static id globalFolderList;
-
- // Global NeXTstep error message table for Wais classes.
- static BOOL waisIsQuiet = NO;
- static id waisStringTable = nil;
-
- // Locks for thread collision protection.
- #ifdef WAIS_THREAD_SUPPORT
- static mutex_t waisTransactionMutex;
- static mutex_t waisFileIOMutex;
-
- // Port for callback to main thread.
- port_t waisCallbackPort = PORT_NULL;
- typedef struct
- {
- msg_header_t hdr;
- msg_type_t type1;
- id callThis;
- msg_type_t type2;
- SEL performThis;
- msg_type_t type3;
- id withThis;
- }
- _WaisCallback, *WaisCallback;
- #endif
-
-
-
- @implementation Wais
-
-
-
- void waisCallbackHandler(WaisCallback msg, void *userData)
- {
- [msg->callThis perform:msg->performThis with:msg->withThis];
- return;
- }
-
- + initialize
- {
- char *buf;
- const char *home, *folder;
- id folderList;
-
- if(self == [Wais class])
- {
- // Grand unified indexed list of Wais objects.
- globalObjectList = [[List alloc] init];
- convertKeyToObject = [[HashTable alloc] initKeyDesc:"%" valueDesc:"@"];
-
- #ifdef WAIS_THREAD_SUPPORT
- // Locks to prevent threads from colliding.
- waisTransactionMutex = mutex_alloc();
- waisFileIOMutex = mutex_alloc();
-
- // Callback port so other threads can ask main thread to
- // perform thread-unsafe actions. This is done by registering
- // the port with the application; the application's main thread
- // picks up the requests via the AppKit event loop. We give
- // the port a high priority since its cargo may be error messages.
- port_allocate(task_self(), &waisCallbackPort);
- DPSAddPort(waisCallbackPort, (DPSPortProc)waisCallbackHandler,
- sizeof(_WaisCallback), NULL, NX_RUNMODALTHRESHOLD);
- #endif
- }
-
- // Create subclass-specific list of folders in which
- // the Wais objects will be found.
- folderList = [[Storage alloc] initCount:0
- elementSize:sizeof(NXAtom) description:"%"];
- if((home=NXHomeDirectory()) && [self defaultHomeFolder])
- {
- if(!(buf = s_malloc(strlen(home)
- + strlen([self defaultHomeFolder]) + 1))) return nil;
- strcpy(buf, home);
- strcat(buf, [self defaultHomeFolder]);
- folder = NXUniqueString(buf);
- s_free(buf);
- }
- else folder = NXUniqueString("/");
- [folderList insert:(void *)&folder at:0];
- if(![self setFolderList:folderList]) return nil;
- return self;
- }
-
- + folderList
- {
- // Recommended subclass method.
- return globalFolderList;
- }
-
- + setFolderList:aList
- {
- // Recommended subclass method.
- if(![aList isKindOf:[Storage class]]) return nil;
- if([aList count] <= 0) return nil;
- if(globalFolderList) [globalFolderList free];
- globalFolderList = aList;
- return self;
- }
-
- + (const char *)defaultHomeFolder
- {
- // Recommended subclass method.
- return "/Library/WAIS";
- }
-
- - (const char *)valueForStringKey:(const char *)aKey
- {
- if(!aKey) return 0;
- return (const char *)[infoFields valueForKey:(void *)aKey];
- }
-
- - (const char *)insertStringKey:(const char *)aKey value:(const char *)aValue
- {
- const char *saveKey, *saveValue;
-
- if(!aKey) return 0;
- if(!aValue) { [infoFields removeKey:(void *)aKey]; return 0; }
- saveKey = NXUniqueString(aKey); saveValue = NXUniqueString(aValue);
- return (const char *)[infoFields
- insertKey:(void *)saveKey value:(void *)saveValue];
- }
-
- + waisObjectList
- {
- return globalObjectList;
- }
-
- + objectForCompleteKey:(const char *)aKey
- {
- id found;
- BOOL quiet;
-
- // Is this a known, loaded object, of right type?
- if(aKey[0] != '/') return nil;
- if(found = (id)[convertKeyToObject valueForKey:(void *)aKey])
- {
- if([found isKindOf:[self class]]) return found;
- else return nil;
- }
-
- // If unknown, try to load it as a file.
- // (Suppress error msgs---just testing.)
- found = [[[self class] alloc] initKey:aKey];
- quiet = [[self class] isQuiet]; [[self class] setQuiet:YES];
- if([found readWaisFile]) { [[self class] setQuiet:quiet]; return found; }
- [[self class] setQuiet:quiet]; [found free];
- return nil;
- }
-
- + objectForKey:(const char *)aKey
- {
- id found, folderList;
- const char **folders;
- char *fullKey;
- int f, len, nfold;
-
- // If complete key, just look up.
- if(!aKey) return nil;
- if(aKey[0] == '/')
- return [[self class] objectForCompleteKey:aKey];
-
- // If partial key, try prepending all folders in folderList.
- if(!(folderList=[[self class] folderList])
- || (nfold=[folderList count])<=0) return nil;
- if(!(folders=(const char **)[folderList elementAt:0]))
- return nil;
- for(f=0; f<nfold; f++)
- {
- if(!folders[f]) continue;
- len = strlen(folders[f]);
- fullKey = s_malloc(len + strlen(aKey) + 2);
- strcpy(fullKey, folders[f]);
- if(fullKey[len-1] != '/') strcat(fullKey, "/");
- strcat(fullKey, aKey);
- if(found = [[self class] objectForCompleteKey:fullKey])
- { s_free(fullKey); return found; }
- s_free(fullKey);
- }
-
- // Not found.
- return nil;
- }
-
- - (const char *)key
- {
- return key;
- }
-
- // Note: in index, this simply overwrites any previous object with same key.
- // Is this what we want?
-
- - setKey:(const char *)aKey
- {
- id folderList;
- const char **folders;
- char *buf;
- int len;
-
- // Make sure it's in the list.
- [globalObjectList addObjectIfAbsent:self];
-
- // Remove object from index if new key is NULL.
- if(!aKey)
- {
- if(key && self==(id)[convertKeyToObject valueForKey:(void *)key])
- [convertKeyToObject removeKey:key];
- key = 0;
- return self;
- }
-
- // If full key, unique it and index it (removing any old entry).
- if(aKey[0] == '/')
- {
- if(key && self==(id)[convertKeyToObject valueForKey:(void *)key])
- [convertKeyToObject removeKey:key];
- key = NXUniqueString(aKey);
- [convertKeyToObject insertKey:(void *)key value:(void*)self];
- return self;
- }
-
- // If partial key, first create full key using default folder.
- if(!(folderList=[[self class] folderList]) || [folderList count]<=0)
- return nil;
- if(!(folders=(const char **)[folderList elementAt:0]))
- return nil;
- if(!folders[0]) return nil;
- len = strlen(folders[0]);
- buf = s_malloc(len + strlen(aKey) + 2);
- strcpy(buf, folders[0]);
- if(buf[len-1] != '/') strcat(buf, "/");
- strcat(buf, aKey);
-
- // Now index it (removing any old entry).
- if(key && self==(id)[convertKeyToObject valueForKey:(void *)key])
- [convertKeyToObject removeKey:key];
- key = NXUniqueString(buf);
- s_free(buf);
- [convertKeyToObject insertKey:(void *)key value:(void*)self];
- return self;
- }
-
- - initKey:(const char *)aKey
- {
- [super init];
- key = 0;
- [self setKey:aKey];
- infoFields = [[HashTable alloc] initKeyDesc:"%" valueDesc:"%"];
- return self;
- }
-
- - free
- {
- // Remove self from indices.
- [convertKeyToObject removeKey:key];
- [globalObjectList removeObject:self];
-
- // Free instance vars (but don't free key---it's an NXAtom).
- [infoFields free];
- return [super free];
- }
-
- - (short)readWaisStruct:(const char *)structName
- forElement:(const char *)elementName
- fromFile:(FILE *)file
- withDecoder:(WaisDecoder)theDecoder
- {
- short check_result;
- static char read_buf[READ_BUF_SIZE];
- char field_name[STRINGSIZE+MAX_SYMBOL_SIZE];
- WaisDecoder decoder;
-
- if(!structName || !theDecoder || !file) return FALSE;
- [Wais lockFileIO];
- if(feof(file)) { [Wais unlockFileIO]; return END_OF_STRUCT_OR_LIST; }
- check_result = CheckStartOfStruct(structName+1, file);
- if(check_result==FALSE || check_result==END_OF_STRUCT_OR_LIST)
- { [Wais unlockFileIO]; return check_result; }
-
- while((check_result=ReadSymbol(field_name, file, MAX_SYMBOL_SIZE))!=FALSE
- && check_result!=END_OF_STRUCT_OR_LIST
- && !feof(file))
- {
- for(decoder=theDecoder; decoder->name; decoder++)
- if(0 == strcmp(field_name, decoder->name))
- {
- switch(decoder->elementType)
- {
- case W_STRUCT:
- [Wais unlockFileIO];
- check_result = [self readWaisStruct:decoder->structName
- forElement:decoder->name fromFile:file
- withDecoder:decoder->subDecoder];
- if(check_result==FALSE
- || check_result==END_OF_STRUCT_OR_LIST)
- return check_result;
- [Wais lockFileIO];
- break;
-
- case W_LIST:
- if((check_result=ReadStartOfList(file))==FALSE
- || check_result==END_OF_STRUCT_OR_LIST)
- { [Wais unlockFileIO]; return check_result; }
- [Wais unlockFileIO];
- do check_result = [self
- readWaisStruct:decoder->structName
- forElement:decoder->name fromFile:file
- withDecoder:decoder->subDecoder];
- while(check_result!=FALSE
- && check_result!=END_OF_STRUCT_OR_LIST);
- if(check_result==FALSE) return check_result;
- [Wais lockFileIO];
- break;
-
- case W_FIELD:
- if(!decoder->reader)
- { SkipObject(file); break; }
- switch(decoder->readArgs)
- {
- case 1:
- check_result = decoder->reader(file);
- break;
- case 2:
- check_result = decoder->reader(read_buf, file);
- break;
- case 3:
- check_result = decoder->reader(read_buf, file,
- MIN(READ_BUF_SIZE, decoder->maxBufSize));
- break;
- default:
- [Wais unlockFileIO]; return FALSE;
- }
- if(check_result==FALSE
- || check_result==END_OF_STRUCT_OR_LIST)
- { [Wais unlockFileIO]; return check_result; }
- if(decoder->readArgs > 1)
- [self insertStringKey:field_name value:read_buf];
- break;
-
- default:
- [Wais unlockFileIO]; return FALSE;
- }
- break;
- }
- if(!decoder->name) SkipObject(file);
- }
- [Wais unlockFileIO];
- return TRUE;
- }
-
- // Subclass method.
- + (const char *)fileStructName
- {
- return ":null";
- }
-
- // Subclass method.
- + (WaisDecoder)fileStructDecoder
- {
- return NULL;
- }
-
- - readWaisFile
- {
- FILE *file;
- short check_result;
-
- // Try to open file specified by key.
- if(!key) return nil;
- [Wais lockFileIO];
- if(!(file = fopen(key, "r")))
- {
- [Wais unlockFileIO];
- ErrorMsg([[self class] errorTitle], "Can't read %s file: %s.",
- [[self class] fileStructName], key);
- return nil;
- }
- [Wais unlockFileIO];
-
- // Read using decoder.
- check_result = [self readWaisStruct:[[self class] fileStructName]
- forElement:[[self class] fileStructName]
- fromFile:file
- withDecoder:[[self class] fileStructDecoder]];
- if(check_result == FALSE)
- {
- [Wais lockFileIO]; fclose(file); [Wais unlockFileIO];
- ErrorMsg([[self class] errorTitle], "Bad %s file format: %s.",
- [[self class] fileStructName], key);
- return nil;
- }
- [Wais lockFileIO]; fclose(file); [Wais unlockFileIO];
- return self;
- }
-
- // Recommended subclass method.
- + (BOOL)checkFileName:(const char *)fileName
- {
- return YES;
- }
-
- // User should free the returned List when done.
- + loadFolder:(const char *)folderName
- {
- int len;
- id w, wlist;
- char filename[MAX_FILENAME_LEN];
- DIR *dirp;
- struct dirent *dp;
-
- // Open directory and loop through its files.
- if(!folderName) return nil;
- [Wais lockFileIO];
- if(!(dirp = opendir(folderName)))
- {
- [Wais unlockFileIO];
- ErrorMsg([[self class] errorTitle],
- "Can't open folder %s.", folderName);
- return nil;
- }
- wlist = [[List alloc] init];
- while(dp=readdir(dirp))
- {
- // Check if file name has correct extension.
- if(![[self class] checkFileName:dp->d_name]) continue;
-
- // If yes, then read it in and put into return list.
- strcpy(filename, folderName);
- len = strlen(filename);
- if(filename[len-1] != '/') strcat(filename, "/");
- strcat(filename, dp->d_name);
- w = [[[self class] alloc] initKey:filename];
- [Wais unlockFileIO];
- if([w readWaisFile]) [wlist addObject:w];
- else [w free];
- [Wais lockFileIO];
- }
- closedir(dirp);
- [Wais unlockFileIO];
- if([wlist count] <= 0) { [wlist free]; return nil; }
- return wlist;
- }
-
- - (short)writeWaisStruct:(const char *)structName
- forElement:(const char *)elementName
- toFile:(FILE *)file
- withDecoder:(WaisDecoder)theDecoder
- {
- short check_result;
- const char *field_value;
- WaisDecoder decoder;
-
- if(!structName || !theDecoder || !file) return NO;
- [Wais lockFileIO];
- WriteStartOfStruct(structName+1, file);
- WriteNewline(file);
-
- for(decoder=theDecoder; decoder->name; decoder++)
- switch(decoder->elementType)
- {
- case W_STRUCT:
- WriteSymbol(decoder->name, file);
- WriteNewline(file);
- [Wais unlockFileIO];
- check_result = [self writeWaisStruct:decoder->structName
- forElement:decoder->name toFile:file
- withDecoder:decoder->subDecoder];
- if(check_result==FALSE) return check_result;
- [Wais lockFileIO];
- break;
-
- case W_LIST:
- WriteSymbol(decoder->name, file);
- WriteNewline(file);
- WriteStartOfList(file);
- [Wais unlockFileIO];
- do check_result = [self
- writeWaisStruct:decoder->structName
- forElement:decoder->name toFile:file
- withDecoder:decoder->subDecoder];
- while(check_result!=FALSE && check_result!=END_OF_STRUCT_OR_LIST);
- if(check_result==FALSE) return check_result;
- [Wais lockFileIO];
- WriteNewline(file);
- WriteEndOfList(file);
- WriteNewline(file);
- break;
-
- case W_FIELD:
- // Don't save empty fields.
- if(!(field_value=[self valueForStringKey:decoder->name])) break;
- if(!field_value[0]) break;
- if(!decoder->writer) break;
- WriteSymbol(decoder->name, file);
- switch(decoder->writeArgs)
- {
- case 1:
- check_result = decoder->writer(file);
- break;
- case 2:
- check_result = decoder->writer(field_value, file);
- break;
- default:
- [Wais unlockFileIO]; return FALSE;
- }
- //!!! The return values of the WriteXXX() routines from
- //!!! the WAIS ir library cannot be trusted. They actually
- //!!! use return values from fprintf(), which are undefined!
- check_result = TRUE;
-
- if(check_result==FALSE)
- { [Wais unlockFileIO]; return check_result; }
- WriteNewline(file);
- break;
-
- default:
- [Wais unlockFileIO]; return FALSE;
- }
-
- WriteEndOfStruct(file);
- WriteNewline(file);
- [Wais unlockFileIO];
- return TRUE;
- }
-
- - writeWaisFile
- {
- FILE *file;
- short check_result;
-
- // Try to create file specified by key.
- if(!key) return nil;
- [Wais lockFileIO];
- if(!(file = fopen(key, "w")))
- {
- [Wais unlockFileIO];
- ErrorMsg([[self class] errorTitle], "Can't create %s file %s.",
- [[self class] fileStructName], key);
- return nil;
- }
- [Wais unlockFileIO];
-
- // Write using decoder.
- check_result = [self writeWaisStruct:[[self class] fileStructName]
- forElement:[[self class] fileStructName]
- toFile:file
- withDecoder:[[self class] fileStructDecoder]];
- if(check_result == FALSE)
- {
- [Wais lockFileIO]; fclose(file); [Wais unlockFileIO];
- ErrorMsg([[self class] errorTitle], "Error writing %s file %s.",
- [[self class] fileStructName], key);
- return nil;
- }
- [Wais lockFileIO]; fclose(file); [Wais unlockFileIO];
- return self;
- }
-
- + lockTransaction
- {
- #ifdef WAIS_THREAD_SUPPORT
- mutex_lock(waisTransactionMutex);
- #endif
- return self;
- }
-
- + unlockTransaction
- {
- #ifdef WAIS_THREAD_SUPPORT
- mutex_unlock(waisTransactionMutex);
- cthread_yield();
- #endif
- return self;
- }
-
- + lockFileIO
- {
- #ifdef WAIS_THREAD_SUPPORT
- mutex_lock(waisFileIOMutex);
- #endif
- return self;
- }
-
- + unlockFileIO
- {
- #ifdef WAIS_THREAD_SUPPORT
- mutex_unlock(waisFileIOMutex);
- cthread_yield();
- #endif
- return self;
- }
-
- + waisNewLocks
- {
- // Locks to prevent threads from colliding.
- //!!! We don't try to recycle old locks.
- waisTransactionMutex = mutex_alloc();
- waisFileIOMutex = mutex_alloc();
- return self;
- }
-
- + callback:anObject perform:(SEL)aSelector with:anArgument
- {
- #ifdef WAIS_THREAD_SUPPORT
- _WaisCallback msg =
- {
- // Note we are just sending object id's and method SEL's.
- { 0, 0, sizeof(_WaisCallback), 0, PORT_NULL, waisCallbackPort, 0},
- { MSG_TYPE_INTEGER_32, 32, 1, TRUE, FALSE, FALSE },
- anObject,
- { MSG_TYPE_INTEGER_32, 32, 1, TRUE, FALSE, FALSE },
- aSelector,
- { MSG_TYPE_INTEGER_32, 32, 1, TRUE, FALSE, FALSE },
- anArgument
- };
- msg_send(&(msg.hdr), 0, 10.0/*timeout*/);
- #else
- [anObject perform:aSelector with:anArgument];
- #endif
- return self;
- }
-
- + (port_t)callbackPort
- {
- return waisCallbackPort;
- }
-
- + setStringTable:aTable
- {
- waisStringTable = aTable;
- return self;
- }
-
- + setQuiet:(BOOL)yn
- {
- waisIsQuiet = yn;
- return self;
- }
-
- + (BOOL)isQuiet
- {
- return waisIsQuiet;
- }
-
- + (const char *)errorTitle
- {
- // Recommended subclass method.
- return "WAIS Error!";
- }
-
- // For internal use only! Use ErrorMsg() function instead.
- + popAlertPanel:errorBuf
- {
- char *title, *errmsg;
- int title_len;
-
- if(!errorBuf) return nil;
- title = (char *)[errorBuf elementAt:0];
- title_len = strlen(title);
- errmsg = (char *)[errorBuf elementAt:title_len+1];
- NXRunAlertPanel(title, errmsg,
- [waisStringTable valueForStringKey:"OK"], NULL, NULL);
- [errorBuf free];
- return self;
- }
-
-
-
- @end
-
-
-
- // -------- UTILITY ROUTINES FOR ERRORS AND WAIS FORMATTED I/O ----------
-
- void ErrorMsg(const char *title, const char *format, ...)
- {
- va_list args;
- id errorBuf;
- int title_len;
-
- if(waisIsQuiet) return;
-
- // Create buffer for passing error title/message to callback.
- errorBuf = [[Storage alloc] initCount:0 elementSize:sizeof(char)
- description:"c"];
- [errorBuf setNumSlots:CHARS_PER_PAGE];//!!!
-
- va_start(args, format);
-
- if(!errorBuf || !waisStringTable
- || ![waisStringTable valueForStringKey:format]
- || ![waisStringTable valueForStringKey:title])
- {
- // Can't use AppKit---enter message in log file instead.
- [errorBuf free];
- [Wais lockFileIO];
- fprintf(logfile, "%s---", title);
- vfprintf(logfile, format, args);
- fprintf(logfile, "\n");
- fflush(logfile);
- [Wais unlockFileIO];
- }
- else
- {
- // Pop up alert panel with AppKit.
- // Use callback since AppKit is not thread safe.
- // +popAlertPanel: will free errorBuf when it's done with it.
- sprintf((char *)[errorBuf elementAt:0],
- "%s", [waisStringTable valueForStringKey:title]);
- title_len = strlen((char *)[errorBuf elementAt:0]);
- vsprintf((char *)[errorBuf elementAt:(title_len+1)],
- [waisStringTable valueForStringKey:format], args);
- [Wais callback:[Wais class]
- perform:@selector(popAlertPanel:) with:errorBuf];
- }
- va_end(args);
- return;
- }
-
-
- long ReadLongS(char *buffer, FILE *file)
- {
- long val, rtn;
-
- rtn = ReadLong(file, &val);
- if(!rtn || rtn==END_OF_STRUCT_OR_LIST) return(rtn);
- sprintf(buffer, "%ld", val);
- return(rtn);
- }
-
- long WriteLongS(char *buffer, FILE *file)
- {
- long val;
-
- if(1 != sscanf(buffer, " %ld ", &val)) return FALSE;
- WriteLong(val, file);
- return TRUE;//!!!
- }
-
- long ReadDoubleS(char *buffer, FILE *file)
- {
- double val;
- long rtn;
-
- rtn = ReadDouble(file, &val);
- if(!rtn || rtn==END_OF_STRUCT_OR_LIST) return(rtn);
- sprintf(buffer, "%lg", val);
- return(rtn);
- }
-
- long WriteDoubleS(char *buffer, FILE *file)
- {
- double val;
-
- if(1 != sscanf(buffer, " %lg ", &val)) return FALSE;
- WriteDouble(val, file);
- return TRUE;//!!!
- }
-
- long ReadListX(FILE *file)
- {
- long rtn;
-
- // Ignore list.
- rtn = ReadStartOfList(file);
- if(!rtn) return(rtn);
- while(getc(file) != ')');
- return(rtn);
- }
-
- void read_subfield(const char *source, char *key, char *value, int value_size)
- {
- char ch;
- long position = 0; /* position in value */
- const char *pos =strstr(source, key); /* address into source */
-
- value[0] = '\0'; /* initialize to nothing */
-
- if(NULL == pos)
- return;
-
- pos = pos + strlen(key);
- ch = *pos;
- /* skip leading quotes and spaces */
- while((ch == '\"') || (ch == ' ')) {
- pos++; ch = *pos;
- }
- for(position = 0; pos < source + strlen(source); pos++){
- if((ch = *pos) == ' ') {
- value[position] = '\0';
- return;
- }
- value[position] = ch;
- position++;
- if(position >= value_size){
- value[value_size - 1] = '\0';
- return;
- }
- }
- value[position] = '\0';
- }
-
- /* right now this hacks out the ^Q/S too. I'll do better later. --j */
- void replace_controlM(char *buffer, long *length)
- {
- char *here, *there, c;
- long i, newlength;
-
- here = there = buffer;
- for(newlength = 0, i = 0; i < *length; i++) {
- c = *here;
- switch (c) {
- case 0:
- *there = 0;
- *length = newlength;
- return;
- case '\r':
- *there = '\n';
- newlength++;
- here++; there++;
- break;
- case 19:
- case 17:
- here++;
- break;
- default:
- *there = *here;
- newlength++;
- here++; there++;
- }
- }
- *length = newlength;
- }
-
- any* copy_any(any *thing)
- {
- int i;
- any* result;
-
- result = NULL;
-
- if(thing != NULL) {
- if((result = (any*)s_malloc(sizeof(any))) != NULL) {
- result->bytes = NULL;
- result->size = thing->size;
- if((result->bytes = s_malloc(thing->size)) != NULL) {
- for(i = 0; i < thing->size; i++)
- result->bytes[i] = thing->bytes[i];
- }
- }
- }
- return result;
- }
-
-
-